package com.example.redis.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.example.redis.common.constant.CacheConstants;
import com.example.redis.domain.UserDO;
import com.example.redis.domain.UserDTO;
import com.example.redis.repository.UserRepository;
import com.example.redis.service.UserService;
import com.google.common.collect.Lists;
import com.pig4cloud.plugin.idempotent.annotation.Idempotent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
@CacheConfig(cacheNames = CacheConstants.USER_DETAILS)
public class UserServiceImpl implements UserService {

    @Resource
    UserRepository userRepository;

    @Autowired
    private CacheManager cacheManager;

    // 穿透：缓存不存在，数据库不存在，高并发，少量key
    // 击穿：缓存不存在，数据库存在，高并发，少量key
    // 雪崩：缓存不存在，数据库存在，高并发，大量key

    @Override
    @Cacheable(key = "#id", unless = "#result==null")
    public UserDO getUserById(Integer id) {
        return userRepository.findUserById(id);
    }

    // #result.get(\"data\") == null


    // 字符串拼接:
    // key = "'wxpublic:weatherOneHour:'+#root.args",
    // key = "#username.concat(#email)",

    // @Cacheable(cacheNames="book", condition="#name.length < 32", unless="#result.name.length > 5"")

    // @Cacheable(value = "info", key = "'info'.concat(#id.toString())")


    @Cacheable(value = CacheConstants.USER_LIST, key = "'list:'+#id", unless = "#result.size()==0")
    public List<String> getUserIdListByGroupId(Long id) {
        List<String> strings = null;
        if (id == 12) {
            strings = Lists.newArrayList("121");
        }
        return strings;
    }

    @Override
    @CacheEvict(key = "#id", condition = "#result>0")
    @Idempotent(key = "#id", expireTime = 3, info = "请勿重复操作")
    public int deleTest(Integer id) {
        log.info("dele-----打印日志------{}", id);
        UserDO userDO = userRepository.findUserById(id);
        int res = 0;
        if (Objects.nonNull(userDO)) {
            userRepository.delete(userDO);
            res = 1;
        }
        return res;
    }

    //mysql默认事务隔离级别：
    // 一个事务在执行过程中可以看到其他事务已经提交的新插入的记录，但是不能看到其他其他事务对已有记录的更新。

    // 1、幻读：事务1读取记录时事务2增加了记录并提交，事务1再次读取时可以看到事务2新增的记录
    // 幻读： 一个事务中多次按相同条件查询，结果不一致。后续查询的结果和面前查询结果不同，多了或少了几行记录。

    // T1 时刻：事务 A 先执行「快照读语句」：select * from t_test where id > 100 得到了 3 条记录。
    // T2 时刻：事务 B 往插入一个 id= 200 的记录并提交；
    // T3 时刻：事务 A 再执行「当前读语句」 select * from t_test where id > 100 就会得到 4 条记录，此时也发生了幻读现象。

    //默认获取锁超时3秒，30秒锁过期
    //支持spel
    @Lock4j(expire = 500, acquireTimeout = 1000, keys = {"#dto.userName"})
    //默认是：executor = RedissonLockExecutor.class
    @Override
    // @Transactional(isolation=Isolation.SERIALIZABLE)
    public void lockTest(UserDTO dto) {
        log.info("模拟超卖场景-----打印日志------{}", dto.toString());

        // 校验范围数据
        Integer money = dto.getMoney();
        List<Integer> list = userRepository.findByMoney(money);

        if (list.size() < 2) {
            UserDO userDO = new UserDO();
            userDO.setUserName("lock测试" + DateUtil.currentSeconds());
            userDO.setPassword(RandomUtil.randomString(6));
            userDO.setAge(22);
            userDO.setMoney(money);
            userRepository.save(userDO);
            log.info("业务执行完成:{}", dto);
        }

    }

    @Override
    // 用户在5秒内只能访问1次
    @Lock4j(keys = {"#dto.userName"}, acquireTimeout = 0, expire = 5000, autoRelease = false)
    public void limitTest(UserDTO dto) {
        log.info("limitTest-----打印日志------{}", dto.toString());
    }


    @Override
    public UserDO getCache(Integer id) {
        Cache cache = getUserCache();
        if (cache != null && cache.get(id) != null) {
            return (UserDO) cache.get(id).get();
        }

        return null;
    }

    @Override
    public void delCache(Integer id) {
        Cache cache = getUserCache();
        if (cache != null) {
            // 立即删除
            cache.evict(id);
        }
        //清空所有缓存
        // cache.clear();

    }

    @Override
    public void updateCache(UserDO userDO) {
        userRepository.save(userDO);
        Cache cache = getUserCache();
        cache.put(userDO.getId(), userDO);
    }


    private Cache getUserCache() {
        return cacheManager.getCache(CacheConstants.USER_DETAILS);
    }

}
